#include "sender.h"
#include "FoICMPstruct.h"
#include <stdio.h>

#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
#define ICMP_ECHO_REQUEST 8
#define SEND_BUFFER_SIZE 4096


SOCKET sender;
struct sockaddr_in dest, source;

void senderConnect (char* filname, char* hostname)
{
    WSADATA wsa;
    struct hostent *host;
    struct in_addr addr;
    int i,j;

    //Initialise Winsock
    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
            printf("WSAStartup() failed.\n");
            return;
    }
    printf("Initialised");

    //Create a RAW Socket
    printf("\nCreating RAW Socket...");

    sender = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, 0);
    if (sender == INVALID_SOCKET)
    {
        printf("Failed to create raw socket.\n");
        return;
    }
    printf("Created.\n");

    //Retrive the available IPs of the local host
    host = gethostbyname(hostname);
    printf("\nAvailable Network Interfaces : \n");
    if (host == NULL)
    {
        printf("Error : %d.\n",WSAGetLastError());
        return;
    }

    for (i = 0; host->h_addr_list[i] != 0; ++i)
    {
	memcpy(&addr, host->h_addr_list[i], sizeof(struct in_addr));
        printf("Interface Number : %d Address : %s\n",i,inet_ntoa(addr));
    }

    memset(&dest, 0, sizeof(dest));
    memcpy(&dest.sin_addr.s_addr,host->h_addr_list[0],sizeof(dest.sin_addr.s_addr));
    dest.sin_family      = host->h_addrtype;
    printf("Socket set.");

    printf("\nStarted Sending\n");

    sendFile(filname);

    closesocket(sender);
    WSACleanup();
}

void sendFile(char* filname)
{
    FILE *fp = NULL;
    long fSize;
    size_t partSize;
    char buffer[SEND_BUFFER_SIZE];
    ICMP_HDR* icmp_hdr = (ICMP_HDR*)buffer;
    FOICMP_HDR *foicmp_hdr = (FOICMP_HDR*) (buffer + sizeof(ICMP_HDR));
    char *data = buffer + sizeof(FOICMP_HDR) + sizeof(ICMP_HDR);;
    USHORT seq_no = 0;
    int packet_size;
    int fpart;

    if ((fp=fopen(filname, "rb")) == NULL) {
        printf ("Failed to open file: %s!\n", filname);
        return;
    }
    fseek (fp, 0, SEEK_END); /* ustawiamy wskaźnik na koniec pliku */
    fSize = ftell(fp);
    rewind (fp);
    
    printf ("File size: %d\n", fSize);
    fpart = FOICMP_PACKET_SIZE - sizeof(ICMP_HDR) - sizeof(FOICMP_HDR);

    while((partSize = fread ( data, 1, fpart, fp)) > 0)
    {
        seq_no += 1;
        packet_size = partSize + sizeof(ICMP_HDR) + sizeof(FOICMP_HDR);

        icmp_hdr->type = ICMP_ECHO_REQUEST;
        icmp_hdr->code = 0;
        icmp_hdr->checksum = 0;
        icmp_hdr->id = (USHORT)GetCurrentProcessId();
        icmp_hdr->seq = htons(seq_no);

        char *fileStr;

        fileStr = strrchr(filname,'\\');
        if (fileStr != NULL)
        {
            fileStr += 1;
        } else {
            fileStr = filname;
        }


        strncpy(foicmp_hdr->fileName, fileStr, sizeof(foicmp_hdr->fileName));
        foicmp_hdr->code = htonl(FOICMP_CODE);
        long pos = ftell(fp) - fpart;
        if ( pos < 0)
            pos = 0;
        foicmp_hdr->ident = htonl(pos);

        icmp_hdr->checksum = checksum((USHORT*)buffer, packet_size );

        int bwrote = sendto(sender, (char*)buffer, packet_size, 0,(struct sockaddr*)&dest, sizeof(dest));
        if (bwrote == SOCKET_ERROR) {
            printf("send failed: %d\n", WSAGetLastError());
            return;
        }
        else 
            printf("sent %d bytes...\n",bwrote);

       fd_set fds;
       int n;
       struct timeval to;

       FD_ZERO(&fds);
       FD_SET(sender, &fds);

       to.tv_sec = RCV_TIMEOUT_SEC;
       to.tv_usec = 0;

       n = select(sender + 1, &fds, NULL, NULL, &to);

       switch(n) {
           case 1 :
           {
                int fromlen = sizeof(source);
                int bread = recvfrom(sender, buffer,
                        sizeof(buffer), 0,
                        (struct sockaddr*)&source, &fromlen);
                if (bread == SOCKET_ERROR) {
                    printf("Read failed.");
                    if (WSAGetLastError() == WSAEMSGSIZE) {
                        printf("buffer too small: %d\n", WSAGetLastError());
                    }
                    else {
                        printf("error: %d\n", WSAGetLastError());
                    }
                }
                else
                {
                    if ( icmp_hdr->seq == seq_no && foicmp_hdr->code == FOICMP_CODE
                            && icmp_hdr->type == ICMP_ECHO_REQUEST );

                            printf("Packet confirmed.\n");
                }
                break;
           }
           case -1:
           {
                printf("Connection error: %d\n", WSAGetLastError());
                break;
           }
           case 0:
           {
                printf("Connection timeout: %d\n", WSAGetLastError());
                break;
           }
           default:
           {
                printf("Receiving error: %d\n", WSAGetLastError());
           }
       }
    }
    fclose (fp);
}

USHORT checksum(USHORT* buffer, int size)
{
    unsigned long cksum = 0;

    while (size > 1) {
        cksum += *buffer++;
        size -= sizeof(USHORT);
    }
    if (size) {
        cksum += *(UCHAR*)buffer;
    }

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);

    return (USHORT)(~cksum);
}

